export interface IState {
    Name: string;

    OnEnter(): void;
    Tick(): void;
    OnExit(): void;
}



class Transition {
    readonly To: IState;
    readonly Condition: () => boolean;

    constructor(to: IState, condition: () => boolean) {
        this.To = to;
        this.Condition = condition;
    }
}


//状态机
export class StateMachine {

    private m_curState: IState;

    private m_transitions = new Map<string, Array<Transition>>();
    private m_curTransitions = new Array<Transition>();
    private m_anyTransitions = new Array<Transition>();

    private static EmptyTransitions = new Array<Transition>();



    Tick(): void {
        let t = this.GetTransition();
        if (t) {
            this.SetState(t.To);
        }

        if (this.m_curState) {
            this.m_curState.Tick();
        }
    }

    GetCurrentState(): IState {
        return this.m_curState; 
    }



    SetState(state: IState): void {
        if (state == this.m_curState) {
            return;
        }

        if (this.m_curState) {
            this.m_curState.OnExit();
        }
        this.m_curState = state;

        this.m_curTransitions = this.m_transitions.get(this.m_curState.Name);
        if (!this.m_curTransitions) {
            this.m_curTransitions = StateMachine.EmptyTransitions;
        }

        this.m_curState.OnEnter();
    }



    AddTransition(from: IState, to: IState, condition: () => boolean): void {
        let transitions: Array<Transition> = this.m_transitions.get(from.Name);
        if (!transitions) {
            transitions = new Array<Transition>();
            this.m_transitions.set(from.Name, transitions);
        }

        transitions.push(new Transition(to, condition));
    }



    AddAnyTransition(state: IState, condition: () => boolean): void {
        this.m_anyTransitions.push(new Transition(state, condition));
    }



    private GetTransition(): Transition {
        for (let t of this.m_anyTransitions) {
            if (t.Condition()) {
                return t;
            }
        }

        for (let t of this.m_curTransitions) {
            if (t.Condition()) {
                return t;
            }
        }

        return null;
    }
}
